#ifndef WAVE_WAVE_FILE_H_
#define WAVE_WAVE_FILE_H_

#include <QScopedPointer>
#include <QString>
#include <QVector>

#include <system_error>

#include "wave/error.h"

namespace QWave {

    enum OpenMode { kIn, kOut };

    class WAVE_API File {
    public:
        File();
        ~File();

        /**
         * @brief Open wave file at given path
         */

        Error Open(const QString &path, OpenMode mode);

        /**
         * @brief Read the entire content of file.
         * @note: File has to be opened in kOut mode or kNotOpen will be returned
         */
        Error Read(QVector<float> *output);

        /**
         * @brief Read the given number of frames from file.
         * @note: File has to be opened in kOut mode or kNotOpen will be returned.
         * If file is too small, kInvalidFormat is returned
         */
        Error Read(qint64 frame_number, QVector<float> *output);

        /**
         * @brief Read and decrypt the entire content of file.
         * @note: File has to be opened in kOut mode or kNotOpen will be returned
         */
        Error Read(void (*decrypt)(char *data, size_t size), QVector<float> *output);
        Error Read(qint64 frame_number, void (*decrypt)(char *data, size_t size),
                   QVector<float> *output);

        /**
         * @brief Write the given data
         * @note: File has to be opened in kIn mode or kNotOpen will be returned.
         * @param clip : if true, hard-clip (force value between -1. and 1.) before writing,
         * else leave data intact. default to false
         */
        Error Write(const QVector<float> &data, bool clip = false);

        /**
         * @brief Write and Encrypt using encryption function
         * @note: File has to be opened in kIn mode or kNotOpen will be returned.
         * @param clip : if true, hard-clip (force value between -1. and 1.) before writing,
         * else leave data intact. default to false
         */
        Error Write(const QVector<float> &data, void (*encrypt)(char *data, size_t size),
                    bool clip = false);

        /**
         * Move to the given frame in the file
         */
        Error Seek(qint64 frame_index);

        /**
         * Give the current frame position in the file
         */
        qint64 Tell() const;

#if __cplusplus >= 201103L
        // C++ 11 available
        File(File &&other);            // Move constructor
        File &operator=(File &&other); // Move assignement operator
#endif                                 // __cplusplus > 201103L

#if __cplusplus > 199711L
        // Modern C++ interface

        // TODO: add std::function version of Read and Write with encrypted
        QVector<float> Read(std::error_code &err);
        QVector<float> Read(qint64 frame_number, std::error_code &err);
        /**
         * @brief Write the given data
         * @param clip : if true, hard-clip (force value between -1. and 1.) before writing,
         * else leave data intact. default to false
         */
        void Write(const QVector<float> &data, std::error_code &err, bool clip = false);

        void Open(const QString &path, OpenMode mode, std::error_code &err);

#endif // __cplusplus > 199711L

        quint16 channel_number() const;
        void set_channel_number(quint16 channel_number);

        quint32 sample_rate() const;
        void set_sample_rate(quint32 sample_rate);

        quint16 bits_per_sample() const;
        void set_bits_per_sample(quint16 bits_per_sample);

        qint64 frame_number() const;

    private:
        class Impl;

        QScopedPointer<Impl> impl_;
    };
} // namespace wave

#endif // WAVE_WAVE_FILE_H_
